/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Slave;

import Core.Dispatcher;
import Core.IdContainer;
import Master.MessageProvider;
import Message.ChangeStatusMessage;
import Message.LogInMessage;
import Message.LogOutMessage;
import Message.SendBuzzMessage;
import Message.SendTextMessage;
import MessageHandler.ChangeStatusMessageHandler;
import MessageHandler.IMessageHandler;
import MessageHandler.IMessageProvider;
import MessageHandler.LogInMessageHandler;
import MessageHandler.LogOutMessageHandler;
import MessageHandler.SendBuzzMessageHandler;
import MessageHandler.SendTextMessageHandler;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Hashtable;

/**
 *
 * @author Mr Bao
 */
public class SlaveServer {

    private int slaveIndex = 0;
    protected int port = 1000;
    protected String address = "localhost";
    protected ServerSocket serverSocket;
    protected boolean isRunning = true;
    protected Dispatcher dispatcher;
    private Thread dispathThread;
    private static IMessageProvider messageProvider;
    private long delayTime = 1000;
    private IdContainer idContainer;
    private Hashtable users = new Hashtable();
    private SlaveInfo slaveInfo;

    static {
        Boot();
    }

    static void Boot() {
        messageProvider = new MessageProvider();
        messageProvider.Register(LogInMessage.class, new LogInMessageHandler());
        messageProvider.Register(LogOutMessage.class, new LogOutMessageHandler());
        messageProvider.Register(SendTextMessage.class, new SendTextMessageHandler());
        messageProvider.Register(SendBuzzMessage.class, new SendBuzzMessageHandler());
        messageProvider.Register(ChangeStatusMessage.class, new ChangeStatusMessageHandler());
    }

    private void LoadConfig() {
        slaveIndex = 1;
        port = 1000;
        address = "localhost";
        delayTime = 1000;
    }

    public IMessageHandler GetMessageHandler(Class type) {
        return messageProvider.GetHandler(type);
    }

    public void SingOff(SlaveClient client) {
        long userId = client.GetUserId();
        synchronized (users) {
            users.remove(userId);
        }
    }

    public SlaveServer(int port) throws IOException {
        LoadConfig();
        this.port = port;
        slaveInfo = new SlaveInfo(slaveIndex, address, this.port);
        idContainer = new IdContainer();
        dispatcher = new Dispatcher();
        dispathThread = new Thread(dispatcher);
        dispathThread.start();
        serverSocket = new ServerSocket(this.port);
        new MasterPinger(users, slaveInfo);
    }

    public SlaveServer() throws IOException {
        LoadConfig();
        slaveInfo = new SlaveInfo(slaveIndex, address, this.port);
        idContainer = new IdContainer();
        dispatcher = new Dispatcher();
        dispathThread = new Thread(dispatcher);
        dispathThread.start();
        serverSocket = new ServerSocket(this.port);
        new MasterPinger(users, slaveInfo);
    }

    public Dispatcher GetDispatcher() {
        return dispatcher;
    }

    public void Halt() {
        isRunning = false;
    }

    public synchronized void AddUser(long userId, String username) {
        users.put(userId, username);
    }

    public void AddClient(long userId, Socket s) {
        try {
            synchronized (users) {
                this.users.put(userId, s);
            }
        } catch (Exception e) {
        }
    }

    public void RemoveClient(long userId) {
        try {
            synchronized (users) {
                this.users.remove(userId);
            }
        } catch (Exception e) {
        }
    }

    public void AcceptConnection() {
        while (isRunning) {
            try {
                Socket socket = serverSocket.accept();
                socket.setTcpNoDelay(true);
                long userId = idContainer.newId();
                new SlaveClient(this, socket, userId);
                AddClient(userId, socket);
            } catch (Exception e) {
                try {
                    Thread.sleep(delayTime);
                } catch (Exception ex) {
                }
            }
        }
    }
}
